package nborm;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

public class NBDao {

    private static final String TAG = "NBDao";

    /**
     * 插入一条数据
     *
     * @param object
     */
    public static void save(Object object) {
        save(object, DBHelper.get().getDb());
    }

    public static void save(Object object, ConflictPolicy policy) {
        if (policy == ConflictPolicy.FORCE_INSERT) {
            save(object, DBHelper.get().getDb());
        } else {
            TableInfo tableInfo = TableHelper.getTableInfo(object.getClass());
            SQLiteDatabase database = DBHelper.get().getDb();
            try (Cursor cursor = database.rawQuery("select * from " + tableInfo.getTableName() +
                    " where " + tableInfo.getTableId() + "=" + tableInfo.getTableIdValue(object), null)) {
                int count = cursor.getCount();
                if (count == 0) {
                    save(object, DBHelper.get().getDb());
                } else {
                    if (policy == ConflictPolicy.REPLACE) {
                        database.update(tableInfo.getTableName(), getValues(object, tableInfo),
                                tableInfo.getTableId() + "=" + tableInfo.getTableIdValue(object), null);
                    } else if (policy == ConflictPolicy.ERROR) {
                        throw new RuntimeException("table:" + tableInfo.getTableName() + ",record already exits:" + tableInfo.getTableIdValue(object));
                    }
                }
            }
        }
    }

    /**
     * 保存对象到数据库。
     *
     * @param object   要保存的对象。
     * @param database 数据库对象
     */
    public static void save(Object object, SQLiteDatabase database) {
        //不要在这里插入集合。
        if (object instanceof List) {
            throw new IllegalArgumentException("You should call saveAll() method");
        }
        TableInfo tableInfo = TableHelper.getTableInfo(object.getClass());
        if (!tableInfo.isTableExists(database)) {
            database.execSQL(TableHelper.getCreateTableSql(object.getClass()));
        }
        save(object, database, tableInfo);
    }

    public static void save(Object object, SQLiteDatabase db, TableInfo tableInfo) {
        db.insert(tableInfo.getTableName(), "nbdao", getValues(object, tableInfo));
    }

    /**
     * 开启事务保存数据。
     *
     * @param tClass 要保存的对象类型
     * @param count  要保存的数量
     * @param action 回调，返回要保存的实体对象
     * @param <T>    类型。
     */
    public static <T> void saveAll(Class<T> tClass, int count, TxAction<T> action) {
        TableInfo tableInfo = TableHelper.getTableInfo(tClass);
        SQLiteDatabase db = DBHelper.get().getDb();
        //不要在这里插入集合。
        if (!tableInfo.isTableExists(db)) {
            db.execSQL(TableHelper.getCreateTableSql(tClass));
        }
        if (db.isOpen()) {
            try {
                //开启事务
                db.beginTransaction();
                for (int i = 0; i < count; i++) {
                    NBDao.save(action.onGetObject(i), db, tableInfo);
                }
            } catch (Exception e) {
                Log.e(TAG, String.valueOf(e));
            } finally {
                db.setTransactionSuccessful();
                db.endTransaction();
            }
        }
    }

    /**
     * 开启事务批量插入
     *
     * @param tClass class
     * @param tArray 数组
     * @param <T>    类型
     */
    public static <T> void saveAll(Class<T> tClass, T[] tArray) {
        TableInfo tableInfo = TableHelper.getTableInfo(tClass);
        SQLiteDatabase db = DBHelper.get().getDb();
        //不要在这里插入集合。
        if (!tableInfo.isTableExists(db)) {
            db.execSQL(TableHelper.getCreateTableSql(tClass));
        }
        if (db.isOpen()) {
            try {
                //开启事务
                db.beginTransaction();
                for (T t : tArray) {
                    NBDao.save(t, db, tableInfo);
                }
            } catch (Exception e) {
                Log.e(TAG, String.valueOf(e));
            } finally {
                db.setTransactionSuccessful();
                db.endTransaction();
            }
        }
    }

    private static ContentValues getValues(Object obj, TableInfo tableInfo) {
        ContentValues contentValues = new ContentValues();
        if (!tableInfo.isAutoincrement()) {
            contentValues.put(tableInfo.getTableId(), String.valueOf(tableInfo.getTableIdValue(obj)));
        }
        for (Map.Entry<String, Field> entry : tableInfo.getColumns().entrySet()) {
            String key = entry.getKey();
            if (tableInfo.getTableId().equals(key))
                continue;
            try {
                Object value = entry.getValue().get(obj);
                if (value == null) {
                    contentValues.put(key, "");
                } else if (TableHelper.isNumber(value.getClass())) {
                    if (value instanceof Byte) {
                        contentValues.put(key, (byte) value);
                    } else if (value instanceof Integer) {
                        contentValues.put(key, (int) value);
                    }
                    if (value instanceof Short) {
                        contentValues.put(key, (short) value);
                    } else if (value instanceof Long) {
                        contentValues.put(key, (long) value);
                    } else if (value instanceof Float) {
                        contentValues.put(key, (float) value);
                    } else if (value instanceof Double) {
                        contentValues.put(key, (double) value);
                    }
                } else if (value.getClass() == Boolean.class || value.getClass() == boolean.class) {
                    contentValues.put(key, (Boolean) value);
                } else if (String.class == value.getClass()) {
                    contentValues.put(key, String.valueOf(value));
                } else if (TableHelper.isCustomField(entry.getValue())) {
                    //对象转json.
                    contentValues.put(key, DBHelper.get().getTypeConverter().toDbString(value));
                }
            } catch (IllegalAccessException e) {
                Log.e(TAG, String.valueOf(e));
            }
        }
        return contentValues;
    }

    /**
     * 插入集合。
     *
     * @param list
     * @param <T>
     */
    public static <T> void saveAll(List<T> list) {
        if (list == null || list.isEmpty())
            return;
        SQLiteDatabase db = DBHelper.get().getDb();
        T first = list.get(0);
        TableInfo tableInfo = TableHelper.getTableInfo(first.getClass());
        if (!tableInfo.isTableExists(db)) {
            db.execSQL(TableHelper.getCreateTableSql(first.getClass()));
        }
        if (db.isOpen()) {
            try {
                //开启事务
                db.beginTransaction();
                for (T t : list) {
                    db.insert(tableInfo.getTableName(), "null", getValues(t, tableInfo));
                }
            } catch (Exception e) {
                Log.e(TAG, String.valueOf(e));
            } finally {
                db.setTransactionSuccessful();
                db.endTransaction();
            }
        }
    }

    /**
     * 批量插入记录 如果存在则不插入
     * 调用此方法请注意性能问题，数据量比较大时请异步调用。
     *
     * @param list
     * @param uniqueColumns uniqueColumns 唯一约束字段，只有当这些字段相同的时候才执行替换操作。
     * @param <T>
     */
    public static <T> void saveAllOrReplace(List<T> list, String... uniqueColumns) {
        if (list == null || list.isEmpty())
            return;
        SQLiteDatabase db = DBHelper.get().getDb();
        T first = list.get(0);
        if (first == null) {
            return;
        }
        TableInfo tableInfo = TableHelper.getTableInfo(first.getClass());
        if (!tableInfo.isTableExists(db)) {
            db.execSQL(TableHelper.getCreateTableSql(first.getClass()));
        }
        if (db.isOpen()) {
            try {
                //开启事务
                db.beginTransaction();
                String[] args = new String[uniqueColumns.length == 0 ? 1 : uniqueColumns.length];
                String[] columns = new String[0];

                String selection = "";
                if (uniqueColumns.length == 0) {
                    selection = tableInfo.getTableId();
                } else {
                    StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < uniqueColumns.length; i++) {
                        String column = uniqueColumns[i];
                        sb.append(column).append(" = ? ");
                        if (i != uniqueColumns.length - 1) {
                            sb.append(" and ");
                        }
                    }
                    selection = sb.toString();
                }
                for (T t : list) {
                    for (int i = 0; i < uniqueColumns.length; i++) {
                        args[i] = String.valueOf(tableInfo.getColumnValue(uniqueColumns[i], t));
                    }
                    Cursor cursor = db.query(tableInfo.getTableName(), columns, selection, args, null, null, null);
                    if (cursor.getCount() > 0) {
                        db.delete(tableInfo.getTableName(), selection, args);
                    }
                    cursor.close();
                    db.insert(tableInfo.getTableName(), "null", getValues(t, tableInfo));
                }
            } catch (Exception e) {
                Log.e(TAG, String.valueOf(e));
            } finally {
                db.setTransactionSuccessful();
                db.endTransaction();
            }
        }
    }

    /**
     * 清空表数据
     *
     * @param tClass
     * @param <T>
     */
    public static <T> void clearTable(Class<T> tClass) {
        TableInfo tableInfo = TableHelper.getTableInfo(tClass);
        DBHelper.get().getDb().execSQL("delete form " + tableInfo.getTableName());
    }

    /**
     * 批量删除。
     *
     * @param list
     * @param <T>
     */
    public static <T> void deleteAll(List<T> list) {
        if (list == null || list.isEmpty())
            return;
        SQLiteDatabase db = DBHelper.get().getDb();
        T first = list.get(0);
        TableInfo tableInfo = TableHelper.getTableInfo(first.getClass());
        if (!tableInfo.isTableExists(db)) {
            return;
        }
        if (db.isOpen()) {
            try {
                //开启事务
                db.beginTransaction();
                String[] whereArgs = new String[]{""};
                for (T t : list) {
                    whereArgs[0] = String.valueOf(tableInfo.getTableIdValue(t));
                    db.delete(tableInfo.getTableName(), tableInfo.getTableId() + "=?",
                            whereArgs);
                }
            } catch (Exception e) {
                Log.e(TAG, String.valueOf(e));
            } finally {
                db.setTransactionSuccessful();
                db.endTransaction();
            }
        }
    }

    /**
     * 获取 表的容量
     *
     * @param cls
     * @param columnName
     * @return 返回数量（-1表示出错）
     */
    public static long getCount(Class<?> cls, @Nullable String columnName) {
        try {
            SQLiteDatabase database = DBHelper.get().getDb();
            TableInfo tableInfo = TableHelper.getTableInfo(cls);
            if (columnName == null) {
                columnName = "*";
            }
            Cursor cursor = database.rawQuery("select count(" + columnName + ") from " + tableInfo.getTableName(), null);
            cursor.moveToFirst();
            long count = cursor.getLong(0);
            cursor.close();
            return count;
        } catch (Exception e) {
            return -1;
        }
    }

    /**
     * 添加或者更新
     *
     * @param object
     */
    public static void saveOrUpdate(Object object) {
        SQLiteDatabase database = DBHelper.get().getDb();
        TableInfo tableInfo = TableHelper.getTableInfo(object.getClass());
        Cursor cursor = null;
        cursor = database.rawQuery("select * from " + tableInfo.getTableName() +
                " where " + tableInfo.getTableId() + "=" + String.valueOf(tableInfo.getTableIdValue(object)), null);
        int count = cursor.getCount();
        if (count > 0) {
            database.update(tableInfo.getTableName(), getValues(object, tableInfo),
                    tableInfo.getTableId() + "=" + tableInfo.getTableIdValue(object), null);
        } else {
            database.insert(tableInfo.getTableName(), "null", getValues(object, tableInfo));
        }
        cursor.close();
    }

    /**
     * 删除
     *
     * @param object
     * @return
     */
    public static int delete(Object object) {
        SQLiteDatabase db = DBHelper.get().getDb();
        TableInfo tableInfo = TableHelper.getTableInfo(object.getClass());
        return db.delete(tableInfo.getTableName(), tableInfo.getTableId()
                + "=?", new String[]{String.valueOf(tableInfo.getTableIdValue(object))});
    }

    public static int delete(Class<?> cls, String whereCause, String[] whereArgs) {
        SQLiteDatabase db = DBHelper.get().getDb();
        TableInfo tableInfo = TableHelper.getTableInfo(cls);
        return db.delete(tableInfo.getTableName(), whereCause, whereArgs);
    }

    public static void dropTable(@Nullable String tableName) {
        try {
            DBHelper.get().getDb().execSQL("drop table " + tableName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void clearTable(String tableName) {
        try {
            DBHelper.get().getDb().execSQL("delete from  " + tableName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 事务回调。
     */
    public interface TxAction<T> {
        T onGetObject(int position);
    }

    public static <T> IQuery<T> query(Class<T> tClass) {
        return new Query<T>(tClass);
    }

    public static <T> IQuery<T> query(Class<T> tClass, @Nullable String[] columns, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String groupBy, @Nullable String having, @Nullable String orderBy, @Nullable String limit) {
        return new Query<T>(tClass, columns, selection, selectionArgs, groupBy, having, orderBy, limit);
    }
}